package com.tanhua.server.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.PageUtil;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.commons.utils.Constants;
import com.tanhua.dubbo.api.CommentApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.VideoApi;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.enums.CommentType;
import com.tanhua.model.mongo.Comment;
import com.tanhua.model.mongo.FocusUser;
import com.tanhua.model.mongo.Video;
import com.tanhua.model.vo.CommentVo;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.PageResult;
import com.tanhua.model.vo.VideoVo;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.interceptor.UserHolder;
import com.tanhua.server.service.VideoService;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.tomcat.util.bcel.Const;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class VideoServiceImpl implements VideoService {

    @DubboReference
    private VideoApi videoApi;

    @Autowired
    private FastFileStorageClient fastFileStorageClient; //用于文件上传或下载

    @Autowired
    private FdfsWebServer fdfsWebServer;//主要用于获取StorageServer的Nginx访问路径

    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @DubboReference
    private UserInfoApi userInfoApi;

    @DubboReference
    private CommentApi commentApi;

    @Autowired
    private MqMessageService mqMessageService;

    /**
     * 视频上传，上传之后清空Redis中的缓存视频列表
     *
     * @param videoThumbnail：视频封面文件
     * @param videoFile：视频文件
     * @CacheEvict(value = "video",allEntries = true)：value指的是缓存的命名空间
     */
    @CacheEvict(value = "video", allEntries = true) //发布视频时，清空缓存
    @Override
    public void uploadVideo(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        //首先判断视频封面或者视频文件是否为空
        if (videoFile.isEmpty() || videoThumbnail.isEmpty()) {
            throw new BusinessException(ErrorResult.error());
        }
        //将视频上传到fastDFS，获取访问url
        //获取视频文件名
        String filename = videoFile.getOriginalFilename();
        //截取字符串，获取视频后缀名，例如：MP4
        String suffix = filename.substring(filename.lastIndexOf(".") + 1);
        //上传文件，并返回文件路径
        StorePath storePath = fastFileStorageClient.uploadFile(videoFile.getInputStream(), videoFile.getSize(), suffix, null);
        //通过webServer获取web端访问地址再加上文件的全路径，得到视频的完整访问路径，即videoUrl
        String videoUrl = fdfsWebServer.getWebServerUrl() + storePath.getFullPath();

        //将视频封面上传到阿里云oss
        String imageUrl = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());

        //构建video对象
        Video video = new Video();
        video.setUserId(UserHolder.getUserId());
        video.setPicUrl(imageUrl);
        video.setVideoUrl(videoUrl);
        video.setText("全民制作人们你们好，我是蔡徐坤，喜欢唱，跳，rap，篮球。");
        //保存数据，返回视频的id作为视频是否保存成功的依据
        String videoId = videoApi.save(video);
        if (StringUtils.isEmpty(videoId)) {
            throw new BusinessException(ErrorResult.error());
        }
        mqMessageService.sendLogMessage(UserHolder.getUserId(),"0301","video",videoId);
    }

    /**
     * 查询视频列表，并将查询出来的结果存入缓存中
     * Redis中key的拼接规则为：value::key
     *
     * @param page
     * @param pagesize
     * @return
     */
    @Cacheable(
            value = "videos",   //value指的是缓存存放的命名空间
            key = "T(com.tanhua.server.interceptor.UserHolder).getUserId()+'_'+#page+'_'+#pagesize")//userId+_+page+_+pagesize
    @Override
    public PageResult videoList(Integer page, Integer pagesize) {
        //查询Redis中的数据
        String redisKey = Constants.VIDEOS_RECOMMEND + UserHolder.getUserId();
        String redisValue = redisTemplate.opsForValue().get(redisKey);
        //判断是否存在数据，判断数据是否满足本次分页条数
        List<Video> list = new ArrayList<>();
        int redisPage = 0;
        redisValue = "";
        if (!StringUtils.isEmpty(redisValue)) {
            //如果数据存在根据Redis中的vid查询数据
            String[] values = redisValue.split(",");
            if ((page - 1) * pagesize < values.length) {
                List<Long> vids = Arrays.stream(values).skip((page - 1) * pagesize).limit(pagesize)
                        .map(e -> Long.valueOf(e))
                        .collect(Collectors.toList());
                //5.调用API根据pid数组查询动态数据
                list = videoApi.findVideoByPids(vids);
            }
            redisPage = PageUtil.totalPage(values.length, pagesize);
        }
        if (list.isEmpty()) {
            //如果数据不存在，分页查询视频数据
            //page计算规则，传入的页码减去Redis中已经查询的总页数
            list = videoApi.queryVideoList(page - redisPage, pagesize);
        }

        List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);
        List<VideoVo> voList = new ArrayList<>();
        for (Video video : list) {
            UserInfo userInfo = map.get(video.getUserId());
            if (userInfo != null) {
                VideoVo vo = VideoVo.init(userInfo, video);
                String key = Constants.VIDEO_LIKE_HASHKEY;
                String hashKey = UserHolder.getUserId().toString() + "_" + video.getId();
                Boolean flag = redisTemplate.opsForHash().hasKey(key, hashKey);
                if (flag) {
                    vo.setHasLiked(1);
                }
                String key1 = Constants.FOCUS_USER_KEY;
                String hashKey1 = UserHolder.getUserId().toString() + "_" + video.getUserId().toString();
                Boolean flag2 = redisTemplate.opsForHash().hasKey(key1, hashKey1);
                if (flag2) {
                    vo.setHasFocus(1);
                }
                voList.add(vo);
            }
        }
        return new PageResult(page, pagesize, 0L, voList);
    }

    /**
     * 视频点赞
     *
     * @param id
     * @CacheEvict ：如果不指定key，那么会将当前方法的全限定名作为key ，然后再与value进行拼接：value::key，allEntries = true 表示删除当前Redis-key下的所有元素
     * 由于Redis中缓存的视频列表的key的规则是： video::当前操作用户的Id+分页参数，但由于其他方法不一定有分页的参数，所以不能通过@CacheEvict注解拼接key来清空缓存
     * @CacheEvict注解不支持模糊删除key，但是RedisTemplate支持模糊删除key
     */
//    @CacheEvict(value = "video",allEntries = true) 
    @Override
    public void videoLike(String id) {
        videoApi.videoLike(id);
        String key = Constants.VIDEO_LIKE_HASHKEY;
        String hashKey = UserHolder.getUserId().toString() + "_" + id;
        String value = "alreadyLike";
        redisTemplate.opsForHash().put(key, hashKey, value);
        //更新操作完成后模糊删除指定的key，@CacheEvict注解无法实现模糊删除
        Set<String> keys = redisTemplate.keys("video" + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 视频取消点赞
     *
     * @param id
     */
//    @CacheEvict(value = "video",allEntries = true)
    @Override
    public void videoDislike(String id) {
        videoApi.videoDislike(id);
        String key = Constants.VIDEO_LIKE_HASHKEY;
        String hashKey = UserHolder.getUserId().toString() + "_" + id;
        redisTemplate.opsForHash().delete(key, hashKey);
        //更新操作完成后模糊删除指定的key
        Set<String> keys = redisTemplate.keys("video" + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 关注用户
     *
     * @param uid：关注的用户的ID
     */
    @Override
    public void userFocus(Long uid) {
        FocusUser focusUser = new FocusUser();
        focusUser.setUserId(UserHolder.getUserId());
        focusUser.setFollowUserId(uid);
        focusUser.setCreated(System.currentTimeMillis());
        videoApi.focus(focusUser);
        //添加关注过的标识
        String key = Constants.FOCUS_USER_KEY;
        String hashKey = UserHolder.getUserId().toString() + "_" + uid.toString();
        redisTemplate.opsForHash().put(key, hashKey, "alreadyFocus");
        //更新操作完成后模糊删除指定的key
        Set<String> keys = redisTemplate.keys("video" + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 取消关注用户
     *
     * @param uid
     */
    @Override
    public void userUnFocus(Long uid) {
        videoApi.Unfocus(UserHolder.getUserId(), uid);
        //删除关注过的标识
        String key = Constants.FOCUS_USER_KEY;
        String hashKey = UserHolder.getUserId().toString() + "_" + uid.toString();
        redisTemplate.opsForHash().delete(key, hashKey);
        //更新操作完成后模糊删除指定的key
        Set<String> keys = redisTemplate.keys("video" + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 对视频发布评论
     *
     * @param id
     * @return
     */
    @Override
    public void comments(String id, String comment) {
        //封装Comment对象
        Comment comment1 = new Comment();
        comment1.setPublishId(new ObjectId(id));
        comment1.setUserId(UserHolder.getUserId());
        comment1.setCommentType(CommentType.COMMENT.getType());
        comment1.setContent(comment);
        comment1.setCreated(System.currentTimeMillis());
        commentApi.saveVideoComments(comment1);

        //更新操作完成后模糊删除指定的key
        Set<String> keys = redisTemplate.keys("video" + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 获取视频评论列表
     * @param id
     * @param page
     * @param pagesize
     * @return
     */
    @Override
    public PageResult commentsList(String id, Integer page, Integer pagesize) {
        //根据视频的id去评论列表中查询全部数据
        List<Comment> list = commentApi.findCommentByVideoId(id,CommentType.COMMENT,page,pagesize);
        if (CollUtil.isEmpty(list)) {
            return new PageResult();
        }
        List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds,null);
        List<CommentVo> voList = new ArrayList<>();
        for (Comment comment : list) {
            UserInfo userInfo = map.get(comment.getUserId());
            if (userInfo!=null){
                CommentVo vo = CommentVo.init(userInfo, comment);
                String key = Constants.MOVEMENTS_INTERACT_KEY + UserHolder.getUserId();
                String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + comment.getId().toHexString();
                if (redisTemplate.opsForHash().hasKey(key,hashKey)){
                    vo.setHasLiked(1);
                }
                voList.add(vo);
            }
        }
        return new PageResult(page,pagesize,0L,voList);
    }

    /**
     * 对视频里的评论点赞
     * @param id：评论ID
     */
    @Override
    public void commentsLike(String id) {
        String key = Constants.MOVEMENTS_INTERACT_KEY + UserHolder.getUserId();
        String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + id;
        //判断是否已经存在key，即判断是否已经点过赞
        Boolean flag = redisTemplate.opsForHash().hasKey(key, hashKey);
        if (flag){
            throw new BusinessException(ErrorResult.likeError());
        }
        Integer integer = commentApi.likeComment(id);
        redisTemplate.opsForHash().put(key,hashKey,"alreadyLike");

        //更新操作完成后模糊删除指定的key
        Set<String> keys = redisTemplate.keys("video" + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 对视频里的评论取消点赞
     * @param id：评论ID
     */
    @Override
    public void commentsDislike(String id) {
        String key = Constants.MOVEMENTS_INTERACT_KEY + UserHolder.getUserId();
        String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + id;
        Boolean flag = redisTemplate.opsForHash().hasKey(key, hashKey);
        if (!flag){
            throw new BusinessException(ErrorResult.disLikeError());
        }
        Integer integer = commentApi.dislikeComment(id);
        redisTemplate.opsForHash().delete(key,hashKey);

        //更新操作完成后模糊删除指定的key
        Set<String> keys = redisTemplate.keys("video" + "*");
        redisTemplate.delete(keys);
    }
}
